﻿using FlyingEye.Application.Exceptions;
using FlyingEye.Application.NetServices;
using FlyingEye.DeviceClients.Requests;
using FlyingEye.DeviceClients.Responses;
using FlyingEye.Exceptions;
using Microsoft.Extensions.Logging;

namespace FlyingEye.DeviceClients
{
    /// <summary>
    /// 设备客户端
    /// </summary>
    public class DeviceClient : IDeviceClient
    {
        // 读写配置锁
        private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);

        // 最大并发数为10，初始化时直接占用所有信号量
        private static readonly SemaphoreSlim _asyncThrottler = new SemaphoreSlim(10, 10);

        private readonly ApplicationContext _context;

        private DeviceManager DeviceManager => _context.DeviceManager;

        private readonly ILogger<DeviceClient> _logger;

        public DeviceClient(ApplicationContext context, ILogger<DeviceClient> logger)
        {
            _context = context;
            _logger = logger;
        }

        /// <summary>
        /// 执行设备配置
        /// </summary>
        public async Task<DefaultResponse> ConfigAsync()
        {
            // 尝试立即获取信号量（0毫秒超时）
            if (!await _asyncThrottler.WaitAsync(TimeSpan.Zero))
            {
                return new DefaultResponse(false, "系统忙碌中，请稍后重试！");
            }

            try
            {
                await _lock.WaitAsync();
                await DeviceManager.CloseAllAsync();
                await DeviceManager.ConnectAsync(_context.ResourceId);

                return new DefaultResponse(true, "设备客户端配置成功！");
            }
            catch (DeviceConnectAggregateException ex)
            {
                _logger.LogInformation(ex, ex.Message);

                var message = string.Empty;

                foreach (var exception in ex.InnerDeviceConnectExceptions)
                {
                    message += exception.Message + "\n";
                }

                return new DefaultResponse(true, message);
            }
            catch (Exception ex)
            {
                _logger.LogInformation(ex, "设备客户端连接错误！");
                return new DefaultResponse(false, "设备客户端连接错误！");
            }
            finally
            {
                _lock.Release();
                _asyncThrottler.Release();
            }
        }

        public async Task<ReadValueResponse> ReadStringValueAsync(ReadStringValueRequest request)
        {
            if (!DeviceManager.TryGetDevice(request.DeviceName, out var device))
            {
                return new ReadValueResponse(false, $"设备 DeviceName = {request.DeviceName} 不存在！", null);
            }

            // 尝试立即获取信号量（0毫秒超时）
            if (!await _asyncThrottler.WaitAsync(TimeSpan.Zero))
            {
                return new ReadValueResponse(false, "系统忙碌中，请稍后重试！", null);
            }

            try
            {
                await _lock.WaitAsync();
                var value = device.ReadString(request.Address, (ushort)request.Length);
                return new ReadValueResponse(true, "读取字符串成功！", value);
            }
            catch (DeviceReadException ex)
            {
                _logger.LogInformation(ex, ex.Message);
                return new ReadValueResponse(false, ex.Message, null);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "数据读取错误！");
                return new ReadValueResponse(false, "数据读取错误！", null);
            }
            finally
            {
                _lock.Release();
                _asyncThrottler.Release();
            }
        }

        public async Task<ReadValueResponse> ReadNumericValueAsync(ReadNumericValueRequest request)
        {
            if (!DeviceManager.TryGetDevice(request.DeviceName, out var device))
            {
                return new ReadValueResponse(false, $"设备 DeviceName = {request.DeviceName} 不存在！", null);
            }

            // 尝试立即获取信号量（0毫秒超时）
            if (!await _asyncThrottler.WaitAsync(TimeSpan.Zero))
            {
                return new ReadValueResponse(false, "系统忙碌中，请稍后重试！", null);
            }

            try
            {
                await _lock.WaitAsync();
                var value = Read(request, device);
                return new ReadValueResponse(true, "读取字符串成功！", value);
            }
            catch (NotSupportedReadException ex)
            {
                _logger.LogInformation(ex, ex.Message);
                return new ReadValueResponse(false, ex.Message, null);
            }
            catch (DeviceReadException ex)
            {
                _logger.LogInformation(ex, ex.Message);
                return new ReadValueResponse(false, ex.Message, null);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "数据读取错误！");
                return new ReadValueResponse(false, "数据读取错误！", null);
            }
            finally
            {
                _lock.Release();
                _asyncThrottler.Release();
            }
        }

        public async Task<DefaultResponse> WriteValueAsync(WriteValueRequest request)
        {
            if (!DeviceManager.TryGetDevice(request.DeviceName, out var device))
            {
                return new DefaultResponse(false, $"设备 DeviceName = {request.DeviceName} 不存在！");
            }

            // 尝试立即获取信号量（0毫秒超时）
            if (!await _asyncThrottler.WaitAsync(TimeSpan.Zero))
            {
                return new ReadValueResponse(false, "系统忙碌中，请稍后重试！", null);
            }

            try
            {
                await _lock.WaitAsync();
                Write(request, device);
                return new DefaultResponse(true, "数据写入成功！");
            }
            catch (NotSupportedWriteException ex)
            {
                _logger.LogInformation(ex, ex.Message);
                return new DefaultResponse(false, ex.Message);
            }
            catch (DeviceWriteException ex)
            {
                _logger.LogInformation(ex, ex.Message);
                return new DefaultResponse(false, ex.Message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "数据写入错误！");
                return new DefaultResponse(false, "数据写入错误！");
            }
            finally
            {
                _lock.Release();
                _asyncThrottler.Release();
            }
        }

        private string Read(ReadNumericValueRequest request, IDevice device)
        {
            return request.DataType switch
            {
                "bool" => device.ReadBool(request.Address).ToString(),
                "byte" => device.ReadByte(request.Address).ToString(),
                "short" => device.ReadShort(request.Address).ToString(),
                "ushort" => device.ReadUshort(request.Address).ToString(),
                "int" => device.ReadInt(request.Address).ToString(),
                "uint" => device.ReadUint(request.Address).ToString(),
                "float" => device.ReadFloat(request.Address).ToString(),
                "double" => device.ReadDouble(request.Address).ToString(),
                _ => throw new NotSupportedReadException(request.DataType)
            };
        }

        private void Write(WriteValueRequest request, IDevice device)
        {
            switch (request.DataType)
            {
                case "bool":
                    device.WriteBool(request.Address, bool.Parse(request.Value));
                    break;
                case "byte":
                    device.WriteByte(request.Address, byte.Parse(request.Value));
                    break;
                case "short":
                    device.WriteShort(request.Address, short.Parse(request.Value));
                    break;
                case "ushort":
                    device.WriteUshort(request.Address, ushort.Parse(request.Value));
                    break;
                case "int":
                    device.WriteInt(request.Address, int.Parse(request.Value));
                    break;
                case "uint":
                    device.WriteUint(request.Address, uint.Parse(request.Value));
                    break;
                case "float":
                    device.WriteFloat(request.Address, float.Parse(request.Value));
                    break;
                case "double":
                    device.WriteDouble(request.Address, double.Parse(request.Value));
                    break;
                case "string":
                    device.WriteString(request.Address, request.Value);
                    break;
                default:
                    throw new NotSupportedWriteException(request.DataType);
            };
        }
    }
}
